#pragma once

#include "Parser.h"

Parser::Parser(PKB* pkb) {
	myPKB = pkb;
	globalStmtCounter = 0;
};

void Parser::startParsing(string filename) {
	
	//initialize variables
	ifstream infile(filename);
	string s;
	int counter = 0;

	//open file stream
	if(!infile.is_open())
	{
		infile.open(filename);
	}
	
	//tokenise the SIMPLE source
	std::vector<std::string> tokens;
	while (infile >> s) {
		tokens.push_back(s);
	}

	try	{
		//Adjust spaces 
		tokens = SpaceRemoval(tokens,"{");
		tokens = SpaceRemoval(tokens,"}");
		tokens = SpaceRemoval(tokens,"+");
		tokens = SpaceRemoval(tokens,"-");
		tokens = SpaceRemoval(tokens,"*");
		tokens = SpaceRemoval(tokens,"=");
		tokens = SpaceRemoval(tokens,";");
		//tokens = SpaceRemovalSemiColon(tokens);
		tokens = BracketSpacer(tokens,"(");
		tokens = BracketSpacer(tokens,")");
		for(int i = 0;i<tokens.size();i++)
	{
		//cout << tokens.at(i) << endl;
	}

		//invoke Procedure function
		int z = Procedure(0,tokens);
		while(z<tokens.size())
		{
			z = Procedure(z,tokens);
		}

	} catch (exception& e) {
		cout << e.what() << '\n';
	}
	myPKB->cfg->refineNext();
	myPKB->cfg->printAll();
	system("PAUSE");
};

vector<string> Parser::BracketSpacer(vector<string> tokens, string delimiter) {
	vector<string> novel;
	size_t found;
	string firstHalf;
	string secondHalf;
	string temp;
	
	for (int i = 0; i < (signed)tokens.size(); i++) {
		temp = tokens.at(i);
		while (temp != "") {
			found = temp.find(delimiter);
			if (found!=std::string::npos) {
				firstHalf = temp.substr(0,found);
				secondHalf = temp.substr(found+1,temp.length()-1);
				if (firstHalf != "") {
					novel.push_back(firstHalf);
				}
				novel.push_back(delimiter);
				temp = secondHalf;
			} else {
				secondHalf = temp;
				temp = "";
				break;
			}
		}
		if (secondHalf != "") {
			novel.push_back(secondHalf);
		}
	}
	return novel;
};

vector<string> Parser::SpaceRemovalSemiColon(vector<string> tokens) {
	vector<string> novel;
	string firstHalf;
	string secondHalf;
	string temp;
	string vada;
	
	for (int i = 0; i < (signed)tokens.size(); i++) {
		if (tokens.at(i) == ";") {
			vada = novel.at(novel.size()-1);
			novel.pop_back();
			novel.push_back(vada+";");
		} else {
			novel.push_back(tokens.at(i));
		}
	}
	return novel;
};

vector<string> Parser::SpaceRemoval(vector<string> tokens, string delimiter) {
	vector<string> novel;
	size_t found;
	string firstHalf;
	string secondHalf;
	string temp;
	
	for (int i = 0; i < (signed)tokens.size(); i++) {
		temp = tokens.at(i);
		while (temp != "") {
			found = temp.find(delimiter);
			if (found!=std::string::npos) {
				firstHalf = temp.substr(0,found);
				secondHalf = temp.substr(found+1,temp.length()-1);
				if (firstHalf != "") {
					novel.push_back(firstHalf);
				}
				novel.push_back(delimiter);
				temp = secondHalf;
			} else {
				secondHalf = temp;
				temp = "";
				break;
			}
		}
		if (secondHalf != "") {
			novel.push_back(secondHalf);
		}
	}
	return novel;
};

int Parser::Procedure(int i, vector<string> tokens) 
{
	match(tokens.at(i), "procedure");
	validateNAME(tokens.at(i+1));
	currProcIndex = myPKB->procTable->insertProc(tokens.at(i+1));
	match(tokens.at(i+2), "{");
	i = StmtLst(i+3, tokens, false, false, -1);
	match(tokens.at(i), "}");
	return i+1;
};

int Parser::StmtLst(int i, vector<string> tokens, bool isWhile, bool isIf, INDEX parentContainer) 
{
	unordered_multimap<INDEX,INDEX> localMapper;
	set<INDEX> localStmtNo;
	INDEX lastStmtInNext = parentContainer;
	bool firstPass = false;
	
	INDEX prev = parentContainer;
	while (!check(tokens.at(i), "}")) 
	{
		++globalStmtCounter;
		if(firstPass)
		{
			myPKB->cfg->forward.insert(std::make_pair<INDEX,INDEX>(prev,globalStmtCounter));
			myPKB->follows->set(prev, globalStmtCounter);
		}
		if (isWhile || isIf) 
		{
			myPKB->parent->set(parentContainer, globalStmtCounter);
		}
		if (isWhile || isIf) 
		{
			myPKB->cfg->forward.insert(std::make_pair<INDEX,INDEX>(prev,globalStmtCounter));
		}
		if(isWhile && !firstPass)
		{
			myPKB->cfg->forward.insert(std::make_pair<INDEX,INDEX>(parentContainer,globalStmtCounter));
		}
		if(isIf && !firstPass)
		{
			myPKB->cfg->forward.insert(std::make_pair<INDEX,INDEX>(parentContainer,globalStmtCounter));
		}
		prev = globalStmtCounter;
		
		INDEX holder = -1;
		INDEX* ptrHolder = &holder;
		i = Stmt(i, tokens, globalStmtCounter,ptrHolder);
		///at this point, I know the variable thT was modified but only concerned with assignment statements
		///so now , have to deal with container statements, and so its ,....
		if(holder!=-1)
		{
			unordered_multimap<INDEX,INDEX>::const_iterator got = localMapper.find(holder);
		
			if(got==localMapper.end())
			{
				localMapper.insert(std::make_pair<INDEX,INDEX>(holder,prev));
			}
			else
			{
				for (auto it2 = localMapper.begin(); it2 != localMapper.end(); ++it2)
				{
					if(it2->first==holder)
					{
						it2->second = prev;
					}
				}
			}
			localStmtNo.insert(prev);
		}
		firstPass = true;
	}
	for (auto it = localStmtNo.begin(); it != localStmtNo.end(); ++it)
	{
		for (auto it2 = localMapper.begin(); it2 != localMapper.end(); ++it2)
		{
			if(myPKB->uses->getIntIdent(*it,myPKB->varTable->getVarName(it2->first)).numRows>0)
			{
				if(myPKB->stmtTable->affectTable.at(*it).size()==0)
				{	
					myPKB->stmtTable->affectTable.at(*it).insert(it2->second);
				}
				else if(myPKB->stmtTable->affectTable.at(*it).size()==1)
				{
					for (auto it3 = myPKB->stmtTable->affectTable.at(*it).begin(); it3 != myPKB->stmtTable->affectTable.at(*it).end(); ++it3)
					{
						if(*it3==-1)
						{
							myPKB->stmtTable->affectTable.at(*it).insert(it2->second);
						}
					}
				}
			}
		}
	}
	if (isWhile || isIf) 
	{
		myPKB->cfg->forward.insert(std::make_pair<INDEX,INDEX>(prev,lastStmtInNext));
	}
	return i;
};

int Parser::Stmt(int i, vector<string> tokens, INDEX stmtNo, INDEX* ptr) 
{
	if (check(tokens.at(i), "while")) {
		return PWhile(i, tokens, stmtNo);
	}
	else if (check(tokens.at(i), "if")) {
		return PIf(i, tokens, stmtNo);
	}
	else if (check(tokens.at(i), "call")) {
		return Call(i, tokens, stmtNo);
	}
	else {
		return Assign(i, tokens, stmtNo, ptr);
	}
};

int Parser::Call(int i, vector<string> tokens, INDEX stmtNo) 
{	
	match(tokens.at(i), "call");
	myPKB->stmtTable->insertStmt(stmtNo, "call");
	//tokens.at(i+1) = tokens.at(i+1).substr(0,tokens.at(i+1).length()-1);
	validateNAME(tokens.at(i+1));
	myPKB->stmtTable->callsProcTable.insert(std::make_pair<INDEX, string>(currProcIndex, tokens.at(i+1)));
	myPKB->stmtTable->callsStmtTable.insert(std::make_pair<INDEX, string>(stmtNo, tokens.at(i+1)));
	return i+3;
};

int Parser::PWhile(int i, vector<string> tokens, INDEX stmtNo) 
{	
		myPKB->stmtTable->insertStmt(stmtNo, "while");
		validateNAME(tokens.at(i+1));
		int controlVarIndex = myPKB->varTable->insertVar(tokens.at(i+1));
		myPKB->ast->whileStmtTable.insert(std::make_pair<INDEX, INDEX>(stmtNo, controlVarIndex));//special while table to store only while statements, used for uses and modifies		
		myPKB->uses->setUsesStmtUses(stmtNo, myPKB->varTable->insertVar(tokens.at(i+1)));//set uses for control variable for while - special caso
		myPKB->uses->setUsesProcUses(currProcIndex, myPKB->varTable->insertVar(tokens.at(i+1)));
		match(tokens.at(i+2), "{");
		i = StmtLst(i+3,tokens,true, false, stmtNo);	
		match(tokens.at(i), "}");
		return i+1;
};

int Parser::PIf(int i, vector<string> tokens, INDEX stmtNo) 
{	
		match(tokens.at(i), "if");
		myPKB->stmtTable->insertStmt(stmtNo, "if");
		validateNAME(tokens.at(i+1));
		int controlVarIndex = myPKB->varTable->insertVar(tokens.at(i+1));
		myPKB->ast->whileStmtTable.insert(std::make_pair<INDEX, INDEX>(stmtNo, controlVarIndex));//special while table to store only while statements, used for uses and modifies		
		myPKB->uses->setUsesStmtUses(stmtNo, myPKB->varTable->insertVar(tokens.at(i+1)));//set uses for control variable for while - special caso
		myPKB->uses->setUsesProcUses(currProcIndex, myPKB->varTable->insertVar(tokens.at(i+1)));
		match(tokens.at(i+2), "then");		
		match(tokens.at(i+3), "{");
		i = StmtLst(i+4,tokens, false, true,stmtNo);
		match(tokens.at(i), "}");
		match(tokens.at(i+1), "else");
		match(tokens.at(i+2), "{");
		i = StmtLst(i+3,tokens, false, true,stmtNo);	
		match(tokens.at(i), "}");
		return i+1;
};

int Parser::Assign(int i, vector<string> tokens, INDEX stmtNo, INDEX* ptr) 
{
	set<INDEX> useSet;
	myPKB->stmtTable->insertStmt(stmtNo, "assign");
	validateNAME(tokens.at(i));
	myPKB->varTable->insertVar(tokens.at(i));
	INDEX modPostponed = myPKB->varTable->insertVar(tokens.at(i));
	*ptr = myPKB->varTable->insertVar(tokens.at(i));
	myPKB->modifies->setModifiesStmtModifies(stmtNo, myPKB->varTable->insertVar(tokens.at(i)));
	myPKB->modifies->setModifiesProcModifies(currProcIndex, myPKB->varTable->insertVar(tokens.at(i)));
	match(tokens.at(i+1), "=");

	int x = i+2;

	vector<string> RHST;
	RHST.clear();
	RHST.shrink_to_fit();
	RHST.push_back("|");

	while (!check(tokens.at(x), ";")) 
	{
		RHST.push_back(tokens.at(x));
		RHST.push_back("|");
		if (checkNAME(tokens.at(x))) 
		{
			myPKB->varTable->insertVar(tokens.at(x));
			myPKB->uses->setUsesStmtUses(stmtNo, myPKB->varTable->insertVar(tokens.at(x)));
			myPKB->uses->setUsesProcUses(currProcIndex, myPKB->varTable->insertVar(tokens.at(x)));
			useSet.insert(myPKB->varTable->getLastMod(myPKB->varTable->insertVar(tokens.at(x))));
		} 
		else if(tokens.at(x)=="+"||tokens.at(x)=="-"||tokens.at(x)=="*"||tokens.at(x)=="("||tokens.at(x)==")"||tokens.at(x)=="{"||tokens.at(x)=="}")
		{
		}
		else
		{
			myPKB->ast->insertConst(stoi(tokens.at(x)));
		}
		++x;
	}
	myPKB->stmtTable->affectTable.insert(std::make_pair<INDEX, set<INDEX>>(stmtNo,useSet));
	myPKB->varTable->updateLastMod(modPostponed,stmtNo);

	myPKB->stmtTable->patternTable.insert(std::make_pair<INDEX, string>(stmtNo, myPKB->stmtTable->buildPatternB(RHST)));
	string te = myPKB->stmtTable->buildPatternB(RHST);
	
	return x+1;
};

class syntaxErrorException: public exception {
  virtual const char* what() const throw(std::invalid_argument) {
	  return "Syntax error in SIMPLE source code";
  }
} syntaxErrorException;

class invalidNAMEException: public exception {
  virtual const char* what() const throw() {
	  return "Invalid NAME in SIMPLE source code";
  }
} invalidNAMEException;

class invalidNAMEorINTEGERException: public exception {
	virtual const char* what() const throw (){
	  return "Invalid NAME or INTEGER in SIMPLE source code";
  }
} invalidNAMEorINTEGERException;

bool Parser::match(string str, string ref) {
	if(str=="*"|str=="-")
	{
		str="+";
	}
	if (str == ref) {
		return true;
	} else {
		throw syntaxErrorException;

	}
};


// dunno why cant throw invalidNAMEException so I change to invalidNAMEorINTEGERException
void Parser::validateNAME(string str) {
	if (!isalpha(str[0])) {
		throw invalidNAMEException;
	}
	for (int i = 0; i < (signed)str.length(); i++) {
		if (!isalnum(str[i])) {
			throw invalidNAMEException;
		}
	}
};

bool Parser::checkNAME(string str) { 
	if (!isalpha(str[0])) {
		return false;
	}
	for (int i = 0; i < (signed)str.length(); i++) {
		if (!isalnum(str[i])) {
			return false;
		}
	}
	return true;
};

void Parser::validateNAMEINTEGER(string str) {
	if (!isalpha(str[0])) {
		stoi(str);
	}
	for (int i = 0; i < (signed)str.length(); i++) {
		if (!isalnum(str[i])) {
			throw invalidNAMEorINTEGERException;
		}
	}
};

bool Parser::check(string str, string ref) {
	if (str == ref) {
		return true;
	} else {
		return false;
	}
};

bool Parser::hasEndsWith(string str, char ref) {
	if (str.back() == ref) {
		return true;
	} else {
		return false;
	}
};